/*******************************************************************************
 * Copyright (c) 2013 Michele Nanna.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Michele Nanna - initial API and implementation
 ******************************************************************************/
package com.systemfastforward.provinciawifi.service;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import au.com.bytecode.opencsv.CSVReader;

/**
 * Simply download and parse the access point list from opendata, and then
 * return this by a list of object representing the Access Point. It caches this
 * list so it download csv only if necessary.
 * 
 * @author Michele Nanna
 * 
 */
public class WebServiceProvinciaWifiCsvDownloader {
	private static final String LOG_TAG = WebServiceProvinciaWifiCsvDownloader.class.getSimpleName();
	private static final String URI_CSV = "http://85.18.173.117/mappe/AccessPoint.csv";
	private static String pathCachedCsv = "accesspoint-cached.csv";
	private static String pathCvsIfFirstTimeNoNetwork = "accesspoint-first-time.csv";

	private static WebServiceProvinciaWifiCsvDownloader instance = null;
	private static Context context;
	private static List<AccessPoint> accessPointList;

	public static WebServiceProvinciaWifiCsvDownloader getInstanceFrom(Context context) {
		if (instance == null) {
			instance = new WebServiceProvinciaWifiCsvDownloader(context);
		}
		return instance;
	}

	private WebServiceProvinciaWifiCsvDownloader(Context context) {
		WebServiceProvinciaWifiCsvDownloader.context = context;
	}

	/**
	 * Get the access point list of POJO, it ensures that this list is always
	 * available, using as file system cache.
	 * 
	 * @return list of POJO representing the access point list
	 */
	public List<AccessPoint> getAccessPointList() {
		downloadCsvIfUpdated();
		parseCsv();
		return accessPointList;
	}

	/**
	 * Download cvs file from network, use cached file if: network is not
	 * available, file not found, server side file is not updated.
	 */
	private void downloadCsvIfUpdated() {
		// check connection, if no exists, return (use cached version)
		ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
		if (networkInfo == null || !networkInfo.isConnected()) {
			Log.i(LOG_TAG, "The network is not available, use cached version");
			return;
		}

		InputStream is = null;
		try {
			URL url = new URL(URI_CSV);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(30000);

			// first check if server side file is modified
			File csvCached = new File(context.getFilesDir().getAbsolutePath() + File.separator + pathCachedCsv);
			conn.setRequestMethod("HEAD");
			conn.setIfModifiedSince(csvCached.lastModified());
			conn.connect();
			if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
				Log.i(LOG_TAG, "The server side file is not modified, use cached version");
				return;
			}
			conn.disconnect();
			conn = (HttpURLConnection) url.openConnection();

			// if file is modified, download last version
			conn.setRequestMethod("GET");
			conn.setDoInput(true);
			conn.connect();
			int response = conn.getResponseCode();
			// if file not found, use cached version
			if (response != HttpURLConnection.HTTP_OK) {
				Log.i(LOG_TAG, "The server side file is not found, use cached version");
				return;
			}
			is = conn.getInputStream();

			// Convert the InputStream to outputstring on disk
			flow(is, context.openFileOutput(pathCachedCsv, Context.MODE_PRIVATE), new byte[1024]);
			Log.i(LOG_TAG, "The server side file has been updated");

			// Makes sure that the InputStream is closed after the app is
			// finished using it.
		} catch (MalformedURLException e) {
			Log.d(LOG_TAG, "Error during http download: " + e.getLocalizedMessage());
		} catch (ProtocolException e) {
			Log.d(LOG_TAG, "Error during http download: " + e.getLocalizedMessage());
		} catch (IOException e) {
			Log.d(LOG_TAG, "Error during http download: " + e.getLocalizedMessage());
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					Log.d(LOG_TAG, "Error during http download: " + e.getLocalizedMessage());
				}
			}
		}
	}

	/**
	 * Read input from input stream and write it to output stream until there is
	 * no more input from input stream.
	 * 
	 * @param is
	 *            input stream the input stream to read from.
	 * @param os
	 *            output stream the output stream to write to.
	 * @param buf
	 *            the byte array to use as a buffer
	 */
	private static void flow(InputStream is, OutputStream os, byte[] buf) throws IOException {
		int numRead;
		while ((numRead = is.read(buf)) >= 0) {
			os.write(buf, 0, numRead);
		}
	}

	/**
	 * Parse cvs file on disk, then creates a List of POJO representing the
	 * access point list.
	 */
	private void parseCsv() {
		accessPointList = new ArrayList<AccessPoint>();

		try {
			InputStream inputStream = null;
			
			// if cached file not exists
			if(!context.getFileStreamPath(pathCachedCsv).exists()){
				inputStream = context.getAssets().open(pathCvsIfFirstTimeNoNetwork);
			}else{
				
				// else using last cached file
				inputStream = context.openFileInput(pathCachedCsv);
			}
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
			CSVReader reader = new CSVReader(inputStreamReader, ',');
			String[] nextLine = reader.readNext();// the first row not used
			while ((nextLine = reader.readNext()) != null) {

				AccessPoint ap = new AccessPoint();
				ap.setDenominazione(nextLine[0]);
				String[] latlon = nextLine[1].split(",");
				double lat = Double.parseDouble(latlon[0]);
				double lon = Double.parseDouble(latlon[1]);
				ap.setLatitudine(lat);
				ap.setLongitudine(lon);
				ap.setIndirizzo(nextLine[2]);
				ap.setComune(nextLine[3]);
				ap.setTipologia(nextLine[4]);

				accessPointList.add(ap);
			}

		} catch (FileNotFoundException e) {
			Log.d(LOG_TAG, "Error during parsing csv: " + e.getLocalizedMessage());
		} catch (IOException e) {
			Log.d(LOG_TAG, "Error during parsing csv: " + e.getLocalizedMessage());
		}
	}
}
